﻿using Fun.Extensions;
using Fun.FontDecode.OpenTypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Fun.FontDecode
{
    /// <summary>
    /// 动态字体解密程序，适用于动态生成的字体里Unicode字符编码与原文不固定的情况。
    /// 原理：得到加密字符对应的图形的所有像素点，比较像素点得到其对应的字符，再将像素点数据与已解密的数据比对，得到原始字符。
    /// </summary>
    public class DynamicFontDecoder : IFontDecoder
    {
        const int ErrorRange = 10;
    
        IDictionary<string, string> FTextMapping;
        KeyValuePair<OpenTypeParser.Coordinate[], string>[] FCoordMapping;
        IDictionary<ushort, OpenTypeParser.Coordinate[]> FCurrMapping = 
            new Dictionary<ushort, OpenTypeParser.Coordinate[]>();

        /// <summary>
        /// 构造。
        /// </summary>
        /// <param name="fontData">TTF 数据</param>
        /// <param name="textMapping">解密字符与图形映射表，coord[]=解密字符，由工具解析生成</param>
        public DynamicFontDecoder(byte[] fontData, IDictionary<string, string> textMapping)
        {
            var parser = new OpenTypeParser();
            parser.Parse(fontData);
            Load(parser, textMapping);
        }

        /// <summary>
        /// 构造。
        /// </summary>
        /// <param name="parser">TTF 解析器</param>
        /// <param name="textMapping">解密字符与图形映射表，coord[]=解密字符，由工具解析生成</param>
        public DynamicFontDecoder(OpenTypeParser parser, IDictionary<string, string> textMapping)
        {
            Load(parser, textMapping);
        }

        void Load(OpenTypeParser parser, IDictionary<string, string> textMapping)
        {
            FTextMapping = textMapping;
            FCoordMapping = FTextMapping
                .Select(x => new KeyValuePair<OpenTypeParser.Coordinate[], string>(OpenTypeParser.Coordinate.FromStrings(x.Key), x.Value))
                .ToArray();

            var cmap = parser.Get<OpenTypeParser.IndexMappingTable>("cmap");
            var glfy = parser.Get<OpenTypeParser.GlyphHeader[]>("glyf");
            foreach (var ch in cmap.CharMaps)
            {
                var points = OpenTypeParser.GetGlyphPolygon(glfy[ch.StartGlyphID]);
                for (var cc = ch.StartCharCode; cc <= ch.EndCharCode; cc++)
                    FCurrMapping.Add((ushort)cc, points);
            }
        }

        static bool Match(OpenTypeParser.Coordinate[] src, OpenTypeParser.Coordinate[] dest, int error)
        {
            if (src.Length != dest.Length)
                return false;

            for (var i = 0; i < src.Length; i++)
            {
                var a = src[i];
                var b = dest[i];
                if (a.IsOnCurve != b.IsOnCurve || Math.Abs(a.X - b.X) > error || Math.Abs(a.Y - b.Y) > error)
                    return false;
            }

            return true;
        }

        public string Decode(string s)
        {
            var chars = StringHelper.ExtractTextAll(s, "&#x", ";")
                .Distinct()
                .Select(x => Convert.ToUInt16(x, 16))
                .ToArray();

            var decodes = new string[chars.Length];
            for (var i = 0; i < chars.Length; i++)
            {
                // 得到当前文字对应的图形
                var points = FCurrMapping[chars[i]];
                if (points == null)
                    return "解码失败(1)。";

                // 根据图形完全匹配
                var found = "";
                var text = OpenTypeParser.Coordinate.ToString(points);
                if (!FTextMapping.TryGetValue(text, out found))
                    // 全匹配失败，则循环查找
                    found = FCoordMapping.FirstOrDefault(x => Match(x.Key, points, ErrorRange)).Value;

                if (string.IsNullOrEmpty(found))
                    return "解码失败(2)。";
                else
                    decodes[i] = found;
            }

            var result = s;
            for (var i = 0; i < chars.Length; i++)
            {
                var fmt = string.Format("{0:X4}", (int)chars[i]);
                result = result.Replace($"&#x{fmt};", decodes[i].ToString());
            }
            return result;
        }
    }
}
